﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using DemoControls;
using LEase;
using PiEase;

namespace PiControls.Controls.Switch
{
    /// <summary>E:\code\C#\PiUi\PiControls\Controls\Switch
    /// 开关按钮
    /// </summary>
    [DefaultEvent("LSwitched")]

    public partial class PiSwitch : UserControl
    {
        /// <summary>
        /// 开关按钮
        /// </summary>
        public PiSwitch()
        {
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            CreateControl();

            tTimer = new System.Windows.Forms.Timer();
            tTimer.Interval = 10;
            tTimer.Tick += TTimer_Tick;

        }
        #region Event
        /// <summary>
        /// 委托
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public delegate void LSwitchedEventHandler(object sender, LEventArgs e);
        /// <summary>
        /// 开关状态发生改变时引发的事件
        /// </summary>
        public event LSwitchedEventHandler LSwitched;
        #endregion

        #region Property

        private Color _CloseColor = Color.FromArgb(224, 224, 224);//浅灰
        /// <summary>
        /// “关”时背景色<para>默认：浅灰</para>
        /// </summary>
        [Category("DemoUI"), Description("“关”时背景色\r\n默认：浅灰")]
        public Color L_CloseColor
        {
            get { return _CloseColor; }
            set
            {
                _CloseColor = value;
                Invalidate();
            }
        }

        private Color _OpenColor = Color.FromArgb(128, 255, 128);//浅绿


      
        /// <summary>
        /// “开”时背景色<para>默认：浅绿</para>
        /// </summary>
        [Category("DemoUI"), Description("“开”时背景色\r\n默认：浅绿")]
        public Color L_OpenColor
        {
            get { return _OpenColor; }
            set
            {
                _OpenColor = value;
                Invalidate();
            }
        }

        private Color _DotColor = Color.White;
        /// <summary>
        /// 圆点颜色<para>默认：白色</para>
        /// </summary>
        [Category("DemoUI"), Description("圆点颜色\r\n默认：白色")]
        public Color L_DotColor
        {
            get { return _DotColor; }
            set
            {
                _DotColor = value;
                Invalidate();
            }
        }

        private int _DotHight = 20;
        /// <summary>
        /// 圆点高度（直径）<para>默认：20</para>
        /// </summary>
        [Category("DemoUI"), Description("圆点高度（直径）\r\n默认：20")]
        public int L_DotHight
        {
            get { return _DotHight; }
            set
            {
                _DotHight = value < 0 ? 0 : value;
                Size = new Size(Width, Math.Max(_DotHight, _BarHight));
                Invalidate();
            }
        }

        private int _BarHight = 24;
        /// <summary>
        /// 背景条高度<para>默认：24</para>
        /// </summary>
        [Category("DemoUI"), Description("背景条高度\r\n默认：24")]
        public int L_BarHight
        {
            get { return _BarHight; }
            set
            {
                _BarHight = value < 0 ? 0 : value;
                Size = new Size(Width, Math.Max(_DotHight, _BarHight));
                Invalidate();
            }
        }

        private bool _IsOpen = false;
        /// <summary>
        /// 开关状态是否是“开”<para>默认：false</para>
        /// </summary>
        [Category("DemoUI"), Description("开关状态是否是“开”\r\n默认：false")]
        public bool L_IsOpen
        {
            get { return _IsOpen; }
            set
            {
                _IsOpen = value;
                Invalidate();
            }
        }

        private int _Margin = 2;
        /// <summary>
        /// 圆点距左右两侧距离<para>默认：2</para>
        /// </summary>
        [Category("DemoUI"), Description("圆点距左右两侧距离\r\n默认：2")]
        public int L_Margin
        {
            get { return _Margin; }
            set
            {
                _Margin = value < 0 ? 0 : value; ;
                Invalidate();
            }
        }

        /// <summary>
        /// 动画效果<para>默认：Quartic</para>
        /// </summary>
        [Category("DemoUI"), Description("动画效果\r\n默认：Quartic")]
        public LEaseType L_AnimateType { get; set; } = (PiEase.LEaseType)LEaseType.Quartic;

        #endregion

        #region Private
        Timer tTimer;
        float fNowLocationBar, fNowLocationDot;
        /// <summary>
        /// 动画效果时长，毫秒
        /// </summary>
        int iDuration = 200;
        int iNow;
        bool bInAnimate = false;
        float fRangeBar, fRangeDot;
        #endregion

        private void TTimer_Tick(object sender, EventArgs e)
        {
            bool b = (_IsOpen ? (iNow >= 0) : (iNow <= iDuration));
            if (b)
            {
                double d = 0;
                switch (L_AnimateType)
                {
                    case LEaseType.Back:
                        d = AnimateBase.Back(Convert.ToDouble(iNow) / iDuration, _IsOpen ? LEaseMode.EaseIn : LEaseMode.EaseOut);
                        break;
                    case LEaseType.Bounce:
                        d = AnimateBase.Bounce(Convert.ToDouble(iNow) / iDuration, _IsOpen ? LEaseMode.EaseIn : LEaseMode.EaseOut);
                        break;
                    case LEaseType.Circle:
                        d = AnimateBase.Circle(Convert.ToDouble(iNow) / iDuration, _IsOpen ? LEaseMode.EaseIn : LEaseMode.EaseOut);
                        break;
                    case LEaseType.Cubic:
                        d = AnimateBase.Cubic(Convert.ToDouble(iNow) / iDuration, _IsOpen ? LEaseMode.EaseIn : LEaseMode.EaseOut);
                        break;
                    case LEaseType.Elastic:
                        d = AnimateBase.Elastic(Convert.ToDouble(iNow) / iDuration, _IsOpen ? LEaseMode.EaseIn : LEaseMode.EaseOut);
                        break;
                    case LEaseType.Exponential:
                        d = AnimateBase.Exponential(Convert.ToDouble(iNow) / iDuration, _IsOpen ? LEaseMode.EaseIn : LEaseMode.EaseOut);
                        break;
                    case LEaseType.Quadratic:
                        d = AnimateBase.Quadratic(Convert.ToDouble(iNow) / iDuration, _IsOpen ? LEaseMode.EaseIn : LEaseMode.EaseOut);
                        break;
                    case LEaseType.Quartic:
                        d = AnimateBase.Quartic(Convert.ToDouble(iNow) / iDuration, _IsOpen ? LEaseMode.EaseIn : LEaseMode.EaseOut);
                        break;
                    case LEaseType.Quintic:
                        d = AnimateBase.Quintic(Convert.ToDouble(iNow) / iDuration, _IsOpen ? LEaseMode.EaseIn : LEaseMode.EaseOut);
                        break;
                    case LEaseType.Sine:
                        d = AnimateBase.Sine(Convert.ToDouble(iNow) / iDuration, _IsOpen ? LEaseMode.EaseIn : LEaseMode.EaseOut);
                        break;
                }

                fNowLocationBar = Convert.ToSingle(d * fRangeBar);
                fNowLocationDot = Convert.ToSingle(d * fRangeDot) + _Margin;
                iNow = _IsOpen ? (iNow - tTimer.Interval) : (iNow + tTimer.Interval);
                Invalidate();
            }
            else
            {
                tTimer.Stop();
                bInAnimate = false;
                LSwitched?.Invoke(this, new LEventArgs(_IsOpen));
            }
        }

        protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
        {
            //为达到较好的视觉效果，宽度至少要是高度的两倍
            int iHeight = Math.Max(_DotHight, _BarHight);
            int iWidth = Math.Max(iHeight * 2, width);
            iHeight++;
            fRangeBar = iWidth - _BarHight;
            fRangeDot = iWidth - _DotHight - _Margin * 2;
            base.SetBoundsCore(x, y, iWidth, iHeight, specified);
        }

        private void PiSwitch_Load(object sender, EventArgs e)
        {

        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            e.Graphics.SmoothingMode = SmoothingMode.HighQuality;

            Pen penBarBack = new Pen(_CloseColor, _BarHight);
            Pen penBarFore = new Pen(_OpenColor, _BarHight);
            penBarBack.StartCap = LineCap.Round;
            penBarBack.EndCap = LineCap.Round;
            penBarFore.StartCap = LineCap.Round;
            penBarFore.EndCap = LineCap.Round;

            float fCapHalfWidth = _BarHight / 2.0f;
            float fLeftTop = Convert.ToSingle(Height - _DotHight) / 2.0f;

            if (!bInAnimate)
            {
                fNowLocationBar = _IsOpen ? 0 : fRangeBar;
                fNowLocationDot = _IsOpen ? _Margin : (fRangeDot + _Margin);
            }

            //画背景
            e.Graphics.DrawLine(penBarBack, fCapHalfWidth, Height / 2f, Width - fCapHalfWidth, Height / 2f);
            //画前景
            e.Graphics.DrawLine(penBarFore, fCapHalfWidth, Height / 2f, Width - fCapHalfWidth - fNowLocationBar, Height / 2f);
            //画圆点
            e.Graphics.FillEllipse(new SolidBrush(_DotColor), Width - _DotHight - fNowLocationDot, fLeftTop, _DotHight, _DotHight);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (!bInAnimate)
            {
                bInAnimate = true;
                _IsOpen = !_IsOpen;
                iNow = _IsOpen ? iDuration : 0;
                tTimer.Start();
            }
        }

    }
}
namespace DemoControls
{
    /// <summary>
    /// 方向
    /// </summary>
    public enum Orientation
    {
        /// <summary>
        /// 水平方向（从左到右）
        /// </summary>
        Horizontal_LR,
        /// <summary>
        /// 水平方向（从右到左）
        /// </summary>
        Horizontal_RL,
        /// <summary>
        /// 垂直方向（从上到上）
        /// </summary>
        Vertical_BT,
        /// <summary>
        /// 垂直方向（从上到下）
        /// </summary>
        Vertical_TB
    }

    /// <summary>
    /// 鼠标状态
    /// </summary>
    public enum MouseStatus
    {
        /// <summary>
        /// 鼠标进入
        /// </summary>
        Enter,
        /// <summary>
        /// 鼠标离开
        /// </summary>
        Leave,
        /// <summary>
        /// 鼠标按下
        /// </summary>
        Down,
        /// <summary>
        /// 鼠标按下释放
        /// </summary>
        Up
    }

    /// <summary>
    /// 滚动条方向
    /// </summary>
    public enum OrientationScrollBar
    {
        /// <summary>
        /// 水平方向
        /// </summary>
        Horizontal,
        /// <summary>
        /// 垂直方向
        /// </summary>
        Vertical
    }
}


namespace DemoControls
{
    /// <summary>
    /// 事件数据
    /// </summary>
    public class LEventArgs : EventArgs
    {
        /// <summary>
        /// 事件数据
        /// </summary>
        /// <param name="value">值</param>
        public LEventArgs(object value)
        {
            Value = value;
        }
        /// <summary>
        /// 值
        /// </summary>
        public object Value { get; set; }
    }

    /// <summary>
    /// 滚动条事件数据
    /// </summary>
    public class LScrollEventArgs : EventArgs
    {
        /// <summary>
        /// 滚动条事件数据
        /// </summary>
        /// <param name="sliderPosition">滑块距顶部（垂直）/左侧（横向）位置</param>
        /// <param name="showPosition">显示位置（正数）</param>
        public LScrollEventArgs(float sliderPosition, float showPosition)
        {
            SliderPosition = sliderPosition;
            ShowPosition = showPosition;
        }

        /// <summary>
        /// 滑块距顶部（垂直）/左侧（横向）位置
        /// </summary>
        public float SliderPosition { get; set; }
        /// <summary>
        /// 显示位置（正数）
        /// </summary>
        public float ShowPosition { get; set; }
    }
}
namespace PiEase {


     public enum LEaseType
    {
        Back,
        Bounce,
        Circle,
        Quadratic,
        Cubic,
        Quartic,
        Quintic,
        Elastic,
        Exponential,
        Sine

    }

    public enum LEaseMode
    {
        EaseIn,
        EaseOut,
        EaseInOut
    }


}




namespace LEase {

   
   
    public class AnimateBase
    {
        public static double Back(double x, LEaseMode easeMode)
        {
            return easeMode switch
            {
                LEaseMode.EaseIn => 2.70158 * x * x * x - 1.70158 * x * x,
                LEaseMode.EaseOut => 1.0 + 2.70158 * Math.Pow(x - 1.0, 3.0) + 1.70158 * Math.Pow(x - 1.0, 2.0),
                LEaseMode.EaseInOut => (x < 0.5) ? (Math.Pow(2.0 * x, 2.0) * (7.189819 * x - 2.5949095) / 2.0) : ((Math.Pow(2.0 * x - 2.0, 2.0) * (7.189819 * x - 4.5949095) + 2.0) / 2.0),
                _ => 0.0,
            };
        }

        private static double BounceOut(double x)
        {
            if (x < 0.36363636363636365)
            {
                return 7.5625 * x * x;
            }

            if (x < 0.72727272727272729)
            {
                return 7.5625 * (x -= 0.54545454545454541) * x + 0.75;
            }

            if (x < 0.90909090909090906)
            {
                return 7.5625 * (x -= 0.81818181818181823) * x + 0.9375;
            }

            return 7.5625 * (x -= 21.0 / 22.0) * x + 63.0 / 64.0;
        }

        public static double Bounce(double x, LEaseMode easeMode)
        {
            return easeMode switch
            {
                LEaseMode.EaseIn => 1.0 - BounceOut(1.0 - x),
                LEaseMode.EaseOut => BounceOut(x),
                LEaseMode.EaseInOut => (x < 0.5) ? ((1.0 - BounceOut(1.0 - 2.0 * x)) / 2.0) : ((1.0 + BounceOut(2.0 * x - 1.0)) / 2.0),
                _ => 0.0,
            };
        }

        public static double Circle(double x, LEaseMode easeMode)
        {
            return easeMode switch
            {
                LEaseMode.EaseIn => 1.0 - Math.Sqrt(1.0 - Math.Pow(x, 2.0)),
                LEaseMode.EaseOut => Math.Sqrt(1.0 - Math.Pow(x - 1.0, 2.0)),
                LEaseMode.EaseInOut => (x < 0.5) ? ((1.0 - Math.Sqrt(1.0 - Math.Pow(2.0 * x, 2.0))) / 2.0) : ((Math.Sqrt(1.0 - Math.Pow(-2.0 * x + 2.0, 2.0)) + 1.0) / 2.0),
                _ => 0.0,
            };
        }

        public static double Power(double x, LEaseMode easeMode, int power)
        {
            return easeMode switch
            {
                LEaseMode.EaseIn => Math.Pow(x, power),
                LEaseMode.EaseOut => 1.0 - Math.Pow(1.0 - x, power),
                LEaseMode.EaseInOut => (x < 0.5) ? (Math.Pow(2.0, power - 1) * Math.Pow(x, power)) : (1.0 - Math.Pow(-2.0 * x + 2.0, power) / 2.0),
                _ => 0.0,
            };
        }

        public static double Quadratic(double x, LEaseMode easeMode)
        {
            return Power(x, easeMode, 2);
        }

        public static double Cubic(double x, LEaseMode easeMode)
        {
            return Power(x, easeMode, 3);
        }

        public static double Quartic(double x, LEaseMode easeMode)
        {
            return Power(x, easeMode, 4);
        }

        public static double Quintic(double x, LEaseMode easeMode)
        {
            return Power(x, easeMode, 5);
        }

        public static double Elastic(double x, LEaseMode easeMode)
        {
            return easeMode switch
            {
                LEaseMode.EaseIn => (x == 0.0) ? 0.0 : ((x == 1.0) ? 1.0 : ((0.0 - Math.Pow(2.0, 10.0 * x - 10.0)) * Math.Sin((x * 10.0 - 10.75) * 2.0 * Math.PI / 3.0))),
                LEaseMode.EaseOut => (x == 0.0) ? 0.0 : ((x == 1.0) ? 1.0 : (Math.Pow(2.0, -10.0 * x) * Math.Sin((x * 10.0 - 0.75) * 2.0 * Math.PI / 3.0) + 1.0)),
                LEaseMode.EaseInOut => (x == 0.0) ? 0.0 : ((x == 1.0) ? 1.0 : ((x < 0.5) ? ((0.0 - Math.Pow(2.0, 20.0 * x - 10.0) * Math.Sin((x * 20.0 - 11.125) * 2.0 * Math.PI / 4.5)) / 2.0) : (Math.Pow(2.0, -20.0 * x + 10.0) * Math.Sin((x * 20.0 - 11.125) * 2.0 * Math.PI / 4.5) / 2.0 + 1.0))),
                _ => 0.0,
            };
        }

        public static double Exponential(double x, LEaseMode easeMode)
        {
            return easeMode switch
            {
                LEaseMode.EaseIn => (x == 0.0) ? 0.0 : Math.Pow(2.0, 10.0 * x - 10.0),
                LEaseMode.EaseOut => (x == 1.0) ? 1.0 : (1.0 - Math.Pow(2.0, -10.0 * x)),
                LEaseMode.EaseInOut => (x == 0.0) ? 0.0 : ((x == 1.0) ? 1.0 : ((x < 0.5) ? (Math.Pow(2.0, 20.0 * x - 10.0) / 2.0) : ((2.0 - Math.Pow(2.0, -20.0 * x + 10.0)) / 2.0))),
                _ => 0.0,
            };
        }

        public static double Sine(double x, LEaseMode easeMode)
        {
            return easeMode switch
            {
                LEaseMode.EaseIn => 1.0 - Math.Cos(x * Math.PI / 2.0),
                LEaseMode.EaseOut => Math.Sin(x * Math.PI / 2.0),
                LEaseMode.EaseInOut => (0.0 - (Math.Cos(Math.PI * x) - 1.0)) / 2.0,
                _ => 0.0,
            };
        }
    }
}